﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

using SSN.Utilities.Database;
using SSN.Utilities.Cache;
using SSN.Utilities.Security;
using SSN.Objects;
using SSN.BusinessFramework.Calendar;

namespace SSN.WebFramework
{
    public class CalendarManager
    {
        private SSNSecurityContext _ctx;
        private static TimeSpan UPCOMING_EVENTS_SPAN = new TimeSpan(21, 0, 0, 0);
        private static KeyValueCache<int, CalendarCategory> _catCache = new KeyValueCache<int, CalendarCategory>(_initCatCache, new TimeSpan(24, 0, 0));
        private static KeyValueCache<int, CalendarEvent> _eventCache = new KeyValueCache<int, CalendarEvent>(_initEventCache, new TimeSpan(0, 5, 0));

        private static Dictionary<int, CalendarCategory> _initCatCache()
        {
            List<CalendarCategory> list = DbManager.BossDb.CreateLoadableListFromQuery<CalendarCategory>(string.Format("SELECT * FROM {0}", CalendarCategory.TableName));
            Dictionary<int, CalendarCategory> table = new Dictionary<int,CalendarCategory>();
            foreach (CalendarCategory cc in list)
            {
                table[cc.ID] = cc;
            }

            return table;
        }

        private static Dictionary<int, CalendarEvent> _initEventCache()
        {
            SystemSecurityContext ctx = new SystemSecurityContext();
            List<CalendarEvent> list = CalendarEvent.DatabaseType.CreateListFromQuery<CalendarEvent>(ctx, string.Format("SELECT * FROM {0}", CalendarEvent.TableName));
            Dictionary<int, CalendarEvent> table = new Dictionary<int, CalendarEvent>();
            foreach (CalendarEvent ce in list)
            {
                table[ce.ID] = ce;
            }

            return table;
        }

        public CalendarManager(SSNSecurityContext context)
        {
            _ctx = context;
        }

        /// <summary>
        /// Purges the calendar cache of events and categories.
        /// </summary>
        public void PurgeCache()
        {
            _catCache.Purge(_ctx);
            _eventCache.Purge(_ctx);
        }

        /// <summary>
        /// Returns a list of all the calendar categories.
        /// </summary>
        public static ReadOnlyCollection<CalendarCategory> Categories
        {
            get { return _catCache.AllValues; }
        }

        /// <summary>
        /// Returns the category with the specified id.
        /// </summary>
        /// <param name="catId">Id of the category to retrieve.</param>
        /// <returns></returns>
        public static CalendarCategory GetCategory(int catId)
        {
            return _catCache[catId];
        }

        /// <summary>
        /// Returns the upcoming events up to the defined constant UPCOMING_EVENTS_SPAN
        /// </summary>
        /// <returns></returns>
        public List<CalendarEvent> GetUpcomingEvents()
        {
            DateTime now = DateTime.Now;
            DateTime then = now.Add(UPCOMING_EVENTS_SPAN);
            return (from ce in _eventCache.AllValues where (ce.Start >= now &&  ce.Start <= then) || (ce.End >= now && ce.End <= then) orderby ce.Start select ce).ToList();
        }

        /// <summary>
        /// Returns the events for a given month an year.
        /// </summary>
        /// <param name="month">Month</param>
        /// <param name="year">Year</param>
        /// <returns></returns>
        public List<CalendarEvent> GetEventsForMonthYear(int month, int year, EventVisibility requiredVisiblity)
        {
            return (from ce in _eventCache.AllValues where ((ce.Start.Month == month && ce.Start.Year == year) || (ce.End.Month == month && ce.End.Year == year)) && ce.Visibility.HasFlag(requiredVisiblity) select ce).ToList();
        }

        /// <summary>
        /// Gets a single event.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public CalendarEvent GetEvent(int id)
        {
            return _eventCache[id];
        }

        public static int BusinessDayDiff(DateTime sd, DateTime ed)
        {
            List<CalendarEvent> holidays = new List<CalendarEvent>();

            DateTime tmp = new DateTime(sd.Year, sd.Month, sd.Day);

            int numBusinessdays = 0;
            while (tmp <= ed)
            {
                if (tmp.DayOfWeek != DayOfWeek.Saturday && tmp.DayOfWeek != DayOfWeek.Sunday)
                {
                    numBusinessdays++;
                }
                tmp.AddDays(1);
            }

            int c = (from h in _eventCache.AllValues
                     where h.Start.Date >= sd.Date && h.Start.Date <= ed.Date && _catCache[h.CategoryID].Name.ToLower() == "holiday" && (h.Start.DayOfWeek != DayOfWeek.Sunday && h.Start.DayOfWeek != DayOfWeek.Sunday)
                     select h).Count();
            return numBusinessdays - c;
        }
    }
}
